package com.dyna.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dyna.constants.ModuleErrorCodeConstants;
import com.dyna.core.exception.CustomException;
import com.dyna.core.utils.CollUtil;
import com.dyna.core.utils.ObjectUtil;
import com.dyna.core.utils.SecurityUtil;
import com.dyna.core.utils.ValidationUtil;
import com.dyna.domain.dto.LoginUser;
import com.dyna.domain.resp.PageResult;
import com.dyna.enums.CommonStatusEnum;
import com.dyna.foundation.domain.bo.UserInfoBo;
import com.dyna.foundation.domain.dto.SignReward;
import com.dyna.foundation.domain.entity.SysDept;
import com.dyna.foundation.domain.entity.SysUser;
import com.dyna.foundation.domain.event.SignRewardEvent;
import com.dyna.foundation.domain.form.SysUserSaveForm;
import com.dyna.foundation.domain.req.SysUserPageReq;
import com.dyna.foundation.domain.resp.SysUserImportExcelResp;
import com.dyna.foundation.domain.resp.SysUserImportResp;
import com.dyna.mapper.SysUserMapper;
import com.dyna.service.SysDeptService;
import com.dyna.service.SysPermissionService;
import com.dyna.service.SysTenantService;
import com.dyna.service.SysUserService;
import com.dyna.utils.RdsUtil;
import com.google.common.annotations.VisibleForTesting;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
* @author Admin
* @description 针对表【user】的数据库操作Service实现
* @createDate 2023-08-13 20:16:58
*/
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    private static final String initPassword = "dyna611";
    private static final String USER_SIGN_RECORD_PREFIX = "user:sign:record:";
    static final String USER_INIT_PASSWORD_KEY = "system.user.init-password";
    static final String USER_REGISTER_ENABLED_KEY = "system.user.register-enabled";

    @Lazy // 延迟，避免循环依赖报错
    @Resource
    private SysTenantService tenantService;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Lazy
    @Resource
    private SysPermissionService sysPermissionService;
    @Resource
    private SysDeptService sysDeptService;
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public UserInfoBo getUserById(Long userId) {
        if (ObjectUtil.isNull(userId)) {
            throw new CustomException("用户id不能为空");
        }
        return baseMapper.queryUserById(userId);
    }

    @Override
    public LoginUser getLoginUserById(Long userId) {
        return baseMapper.queryLoginUserById(userId);
    }

    @Override
    public List<UserInfoBo> getUserByIds(List<Long> userIdList) {
        if (CollUtil.isEmpty(userIdList)) {
            throw new CustomException("用户id不能为空");
        }
        return baseMapper.queryUserByIds(userIdList);
    }

    @Override
    public SysUser getUserByUsername(String username) {
        SysUser sysUser = baseMapper.queryUserByUsername(username);
        return sysUser;
    }

    @Override
    public Long sign() {
        // 使用redis的bitmap进行保存用户签到
        Map<String, Object> map = signKey();
        String key = (String) map.get("key");
        Integer dayOfMonth = (Integer) map.get("dayOfMonth");
        // 是否已签到
        Boolean signFlag = RdsUtil.opsForValue().getBit(key, dayOfMonth);
        //   已签到直接返回
        if (Boolean.TRUE.equals(signFlag)) {
            // 0不能重复签到
            throw new CustomException("已签到成功");
        }

        // 未签到，进行签到
        RdsUtil.opsForValue().setBit(key, dayOfMonth, true);
        // 查询连续登录天数,通过Spring异步事件驱动发送奖励
        Long continuousSignCount = RdsUtil.getContinuousSignCount(key, dayOfMonth);
        LoginUser userInfo = SecurityUtil.getUserInfo();
        applicationEventPublisher.publishEvent(new SignRewardEvent(
                new SignReward(userInfo.getUserId(), userInfo.getUsername(), continuousSignCount)
        ));
        // 重复签到天数
        return continuousSignCount;
    }

    private Map<String, Object> signKey() {
        LoginUser userInfo = SecurityUtil.getUserInfo();
        Long userId = userInfo.getUserId();
        LocalDateTime now = LocalDateTime.now();
        int dayOfMonth = now.getDayOfMonth() - 1;
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = USER_SIGN_RECORD_PREFIX + userId + keySuffix;
        // 用户签到记录
        Map<String, Object> map = new HashMap<>(2);
        map.put("key", key);
        map.put("dayOfMonth", dayOfMonth);
        return map;
    }

    @Override
    public Boolean cancelSign() {
        Map<String, Object> map = signKey();
        String key = (String) map.get("key");
        Integer dayOfMonth = (Integer) map.get("dayOfMonth");
        // 设置为false，取消用户签到
        return RdsUtil.opsForValue().setBit(key, dayOfMonth, false);
    }

    @Override
    public List<UserInfoBo> getUserByRoleId(Long roleId) {
        Optional.ofNullable(roleId).orElseThrow(()-> new CustomException("角色id不能为空"));
        return baseMapper.queryUserByRoleId(roleId);
    }

    @Override
    public Boolean testDel(Long userId) {
        try {
            int i = 1 / 0;
        }catch (Exception e) {
            throw new CustomException("捕获异常");
        }
        return removeById(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createUser(SysUserSaveForm createReqVO) {
        // 1.1 校验账户配合
        tenantService.handleTenantInfo(tenant -> {
            long count = baseMapper.selectCount();
            if (count >= tenant.getAccountCount()) {
                throw new CustomException(StrUtil.format(ModuleErrorCodeConstants.USER_COUNT_MAX.getMsg(), tenant.getAccountCount()));
            }
        });
        // 1.2 校验正确性
        validateUserForCreateOrUpdate(null, createReqVO.getUsername(),
                createReqVO.getPhone(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds());
        // 2.1 插入用户
        SysUser user = BeanUtil.toBean(createReqVO, SysUser.class);
        user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
        user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码
        baseMapper.insert(user);
        return user.getUserId();
    }

    private SysUser validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
                                                      Long deptId, Set<Long> postIds) {
        // 校验用户存在
        SysUser user = validateUserExists(id);
        // 校验用户名唯一
        validateUsernameUnique(id, username);
        // 校验手机号唯一
        validateMobileUnique(id, mobile);
        // 校验部门处于开启状态
//        deptService.validateDeptList(CollectionUtils.singleton(deptId));
        return user;
    }

    @VisibleForTesting
    SysUser validateUserExists(Long userId) {
        if (userId == null) {
            return null;
        }
        SysUser user = baseMapper.selectById(userId);
        if (user == null) {
            throw new CustomException(ModuleErrorCodeConstants.USER_NOT_EXISTS.getMsg());
        }
        return user;
    }

    @VisibleForTesting
    void validateUsernameUnique(Long id, String username) {
        if (StrUtil.isBlank(username)) {
            return;
        }
        SysUser user = baseMapper.selectByUsername(username);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw new CustomException(ModuleErrorCodeConstants.USER_USERNAME_EXISTS.getMsg());
        }
        if (!user.getUserId().equals(id)) {
            throw new CustomException(ModuleErrorCodeConstants.USER_USERNAME_EXISTS.getMsg());
        }
    }

    @VisibleForTesting
    void validateMobileUnique(Long id, String phone) {
        if (StrUtil.isBlank(phone)) {
            return;
        }
        SysUser user = baseMapper.selectByPhone(phone);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw new CustomException(ModuleErrorCodeConstants.USER_MOBILE_EXISTS.getMsg());
        }
        if (!user.getUserId().equals(id)) {
            throw new CustomException(ModuleErrorCodeConstants.USER_MOBILE_EXISTS.getMsg());
        }
    }

    /**
     * 校验旧密码
     * @param id          用户 id
     * @param oldPassword 旧密码
     */
    @VisibleForTesting
    void validateOldPassword(Long id, String oldPassword) {
        SysUser user = baseMapper.selectById(id);
        if (user == null) {
            throw new CustomException(ModuleErrorCodeConstants.USER_NOT_EXISTS.getMsg());
        }
        if (!isPasswordMatch(oldPassword, user.getPassword())) {
            throw new CustomException(ModuleErrorCodeConstants.USER_PASSWORD_FAILED.getMsg());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(Long id) {
        // 1. 校验用户存在
        SysUser user = validateUserExists(id);

        // 2.1 删除用户
        baseMapper.deleteById(id);
        // 2.2 删除用户关联数据
        sysPermissionService.processUserDeleted(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(SysUserSaveForm updateReqVO) {
        updateReqVO.setPassword(null); // 特殊：此处不更新密码
        // 1. 校验正确性
        SysUser oldUser = validateUserForCreateOrUpdate(updateReqVO.getUserId(), updateReqVO.getUsername(),
                updateReqVO.getPhone(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds());

        // 2.1 更新用户
        SysUser updateObj = BeanUtil.toBean(updateReqVO, SysUser.class);
        baseMapper.updateById(updateObj);
    }

    @Override
    public void updateUserPassword(Long userId, String password) {
        // 1. 校验用户存在
        SysUser user = validateUserExists(userId);

        // 2. 更新密码
        SysUser updateObj = new SysUser();
        updateObj.setUserId(userId);
        updateObj.setPassword(encodePassword(password)); // 加密密码
        baseMapper.updateById(updateObj);
    }

    @Override
    public void updateUserStatus(Long id, Integer status) {
        // 校验用户存在
        validateUserExists(id);
        // 更新状态
        SysUser updateObj = new SysUser();
        updateObj.setUserId(id);
        updateObj.setStatus(status);
        baseMapper.updateById(updateObj);
    }

    @Override
    public List<SysUser> getUserListByStatus(Integer status) {
        return baseMapper.selectListByStatus(status);
    }

    @Override
    public PageResult<SysUser> getUserPage(SysUserPageReq reqVO) {
        // 如果有角色编号，查询角色对应的用户编号
        Set<Long> userIds = reqVO.getRoleId() != null ?
                sysPermissionService.getUserRoleIdListByRoleId(CollUtil.singleton(reqVO.getRoleId())) : null;

        // 分页查询
        return baseMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds);
    }

    /**
     * 获得部门条件：查询指定部门的子部门编号们，包括自身
     *
     * @param deptId 部门编号
     * @return 部门编号集合
     */
    private Set<Long> getDeptCondition(Long deptId) {
        if (deptId == null) {
            return Collections.emptySet();
        }
        Set<Long> deptIds = CollUtil.convertSet(sysDeptService.getChildDeptList(deptId), SysDept::getDeptId);
        deptIds.add(deptId); // 包括自身
        return deptIds;
    }

    @Override
    public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    /**
     * 对密码进行加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    @Override
    public SysUser getUser(Long userId) {
        return baseMapper.selectById(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入
    public SysUserImportResp importUserList(List<SysUserImportExcelResp> importUsers, boolean isUpdateSupport) {
        // 1.1 参数校验
        if (CollUtil.isEmpty(importUsers)) {
            throw new CustomException(ModuleErrorCodeConstants.USER_IMPORT_LIST_IS_EMPTY.getMsg());
        }

        // 2. 遍历，逐个创建 or 更新
        SysUserImportResp respVO = SysUserImportResp.builder().createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build();
        importUsers.forEach(importUser -> {
            // 2.1.1 校验字段是否符合要求
            try {
                SysUserSaveForm sysUserSaveForm = BeanUtil.toBean(importUser, SysUserSaveForm.class);
                sysUserSaveForm.setPassword(initPassword);
                ValidationUtil.validate(sysUserSaveForm);
            } catch (Exception ex){
                respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
                return;
            }
            // 2.1.2 校验，判断是否有不符合的原因
            try {
                validateUserForCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(),
                        importUser.getDeptId(), null);
            } catch (Exception ex) {
                respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
                return;
            }

            // 2.2.1 判断如果不存在，在进行插入
            SysUser existUser = baseMapper.selectByUsername(importUser.getUsername());
            if (existUser == null) {
                SysUser sysUser = BeanUtil.toBean(importUser, SysUser.class);
                sysUser.setPassword(encodePassword(initPassword));
                baseMapper.insert(sysUser); // 设置默认密码及空岗位编号数组
                respVO.getCreateUsernames().add(importUser.getUsername());
                return;
            }
            // 2.2.2 如果存在，判断是否允许更新
            if (!isUpdateSupport) {
                respVO.getFailureUsernames().put(importUser.getUsername(), ModuleErrorCodeConstants.USER_USERNAME_EXISTS.getMsg());
                return;
            }
            SysUser updateUser = BeanUtil.toBean(importUser, SysUser.class);
            updateUser.setUserId(existUser.getUserId());
            baseMapper.updateById(updateUser);
            respVO.getUpdateUsernames().add(importUser.getUsername());
        });
        return respVO;
    }


}




